import {
    ExecutionContext,
    Injectable,
    UnauthorizedException,
    NotFoundException,
  } from '@nestjs/common';
  import { AuthGuard } from '@nestjs/passport';
  import { IS_PUBLIC_KEY } from '../decorators/public';
  import { Reflector } from '@nestjs/core';
  import { InjectRedis } from '@nestjs-modules/ioredis';
  import Redis from 'ioredis';
  
  @Injectable()
  export class JwtAuthGuard extends AuthGuard('jwt') {
    constructor(
      private reflector: Reflector,
      @InjectRedis() private readonly redis: Redis,
    ) {
      super();
    }
  
    /** 验证token */
    async canActivate(
      context: ExecutionContext,
    ): Promise<boolean> {
      // 是否是公共路由
      const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
        context.getHandler(),
        context.getClass(),
      ]);
      if (isPublic) return true;
  
      // 获取请求对象
      const request = context.switchToHttp().getRequest();
      const token = this.extractTokenFromHeader(request);
  
      if (!token) {
        throw new UnauthorizedException('请登录后访问');
      }
  
      // 检查令牌是否在黑名单中
      const isBlacklisted = await this.redis.exists(`user:blacklist:${token}`);
      if (isBlacklisted) {
        throw new UnauthorizedException('会话已过期，请重新登录');
      }
      
      // 校验token
      // 修改这里：调用父类的canActivate并等待其结果
      const result = await super.canActivate(context);
      return result as boolean;
    }
  
    /**
     * @description: 验完成后调用
     * @param {*} error 这是 Passport 策略执行过程中发生的任何潜在错误。如果在验证过程中没有错误发生，这个值通常是 null
     * @param {*} user 这是 Passport 策略验证成功后返回的用户对象。如果验证失败，这个值可能是 false 或 null，具体取决于你使用的 Passport 策略
     * @param {*} info 如果验证失败，info通常是一个error对象
     * @Date: 2024-01-02 13:14:47
     * @Author: mulingyuer
     */
    handleRequest(error, user, info) {
      if (info || error) throw new UnauthorizedException('token校验失败');
      if (!user) throw new NotFoundException('用户不存在');
  
      return user;
    }
  
    private extractTokenFromHeader(request: any): string | undefined {
      const [type, token] = request.headers.authorization?.split(' ') ?? [];
      return type === 'Bearer' ? token : undefined;
    }
  }